package com.ld.shieldsb.common.core.util.notice.wechat.webhook;

import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.ld.shieldsb.common.core.collections.ListUtils;
import com.ld.shieldsb.common.core.io.FileUtils;
import com.ld.shieldsb.common.core.model.PropertiesModel;
import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.pattern.PatternUtil;
import com.ld.shieldsb.common.core.reflect.ModelUtil;
import com.ld.shieldsb.common.core.util.JsoupUtil;
import com.ld.shieldsb.common.core.util.ResultUtil;
import com.ld.shieldsb.common.core.util.StringUtils;
import com.ld.shieldsb.common.core.util.notice.wechat.EweChatTool;
import com.ld.shieldsb.common.core.util.notice.wechat.application.tmplcard.TemplateCardBasicModel;
import com.ld.shieldsb.common.core.util.notice.wechat.application.tmplcard.TmplCardActionModel;
import com.ld.shieldsb.common.core.util.notice.wechat.application.tmplcard.TmplCardMainTitleModel;
import com.ld.shieldsb.common.core.util.notice.wechat.application.tmplcard.news.TemplateCardNewsModel;
import com.ld.shieldsb.common.core.util.notice.wechat.application.tmplcard.news.TmplCardImageModel;
import com.ld.shieldsb.common.core.util.notice.wechat.application.tmplcard.text.TemplateCardTextModel;
import com.ld.shieldsb.common.core.util.notice.wechat.log.EweChatLogFactory;
import com.ld.shieldsb.common.core.util.notice.wechat.log.EweChatLogModel;
import com.ld.shieldsb.common.core.util.notice.wechat.model.NewsMsgModel;

import lombok.extern.slf4j.Slf4j;

/**
 * 用于企业微信群机器人的工具类，需要先创建机器人，迁移在工作平台项目
 * 
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2021年4月15日 下午5:16:18
 *
 */
@Slf4j
public class EweChatWebHookUtil {
    public static final String CONTENT = "content"; // 消息内容
    public static final String USERID_LIST = "mentioned_list"; // 发送给的用户
    public static final String MOBILE_LIST = "mentioned_mobile_list"; // 发送给的用户手机号列表

    public static final String CONFIG_KEY = "notice.ewechatbot.client-secret";

    public static final String URL_SEND = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s"; // 发送信息的url，%s为占位符,client-secret
    public static final String URL_UPLOAD = "https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key=%s&type=%s&debug=1"; // 发送信息的url，%s为占位符

    public static final String UPLOAD_FILE_NAME = "media"; // 上传文件名
    public static final String UPLOAD_RESULT_FILE_ID = "media_id"; // 返回的文件编号

    enum Type {
        /*  自定义机器人支持文本（text）、markdown（markdown）、图片（image）、图文（news）四种消息类型。
                                机器人的text/markdown类型消息支持在content中使用<@userid>扩展语法来@群成员     */

        /*文本*/TEXT("text"), MARKDOWN("markdown"), IMAGE("image"), /*图文*/ NEWS("news"), /*文件*/FILE(
                "file"), /*模板卡片，投票选择型和多项选择型卡片仅企业微信3.1.12及以上版本支持
                         文本通知型、图文展示型和按钮交互型三种卡片仅企业微信3.1.6及以上版本支持（但附件下载功能仍需更新至3.1.12）*/TEMPLATE_CARD("template_card");

        private final String value;

        Type(final String val) {
            this.value = val;
        }

        public String getValue() {
            return this.value;
        }

    }

    // ============================================= 文本消息 ==============================================
    /**
     * 转换为文本消息的map
     * 
     * @Title parse2TextDataMap
     * @author 吕凯
     * @date 2020年7月3日 上午9:27:26
     * @param content
     *            内容，支持@某个人，文本内容，最长不超过2048个字节，必须是utf8编码
     * @return Map<String,Object>
     */
    public static final Map<String, Object> parse2TextDataMap(String content) {
        EweChatWebHookMsgParams params = EweChatWebHookMsgParams.builder().content(content).build();
        return parse2TextDataMap(params);
    }

    public static final Map<String, Object> parse2TextDataMap(String content, String noticeIds, String noticeMobiles) {
        EweChatWebHookMsgParams params = EweChatWebHookMsgParams.builder().content(content).noticeIds(noticeIds)
                .noticeMobiles(noticeMobiles).build();
        return parse2TextDataMap(params);
    }

    public static final Map<String, Object> parse2TextDataMap(String content, boolean noticeAll) {
        EweChatWebHookMsgParams params = EweChatWebHookMsgParams.builder().content(content).noticeAll(noticeAll).build();
        return parse2TextDataMap(params);
    }

    /**
     * 
     * 转换为文本消息的map
     * 
     * @author 吕凯
     * @date 2020年7月6日 下午4:55:10
     * @param params
     *            参数
     * @return Map<String,Object>
     */
    public static final Map<String, Object> parse2TextDataMap(EweChatWebHookMsgParams params) {
        String content = params.getContent();
        /*{
            "msgtype": "text",
            "text": {
                CONTENT: "hello world"
            }
        }*/
        /*{
            "msgtype": "text",
            "text": {
                CONTENT: "广州今日天气：29度，大部分多云，降雨概率：60%",
                "mentioned_list":["wangqing","@all"],
                "mentioned_mobile_list":["13800001111","@all"]
            }
        }*/
        /*msgtype   是   消息类型，此时固定为text
        content 是   文本内容，最长不超过2048个字节，必须是utf8编码
        mentioned_list  否   userid的列表，提醒群中的指定成员(@某个成员)，@all表示提醒所有人，如果开发者获取不到userid，可以使用mentioned_mobile_list
        mentioned_mobile_list   否   手机号列表，提醒手机号对应的群成员(@某个成员)，@all表示提醒所有人*/

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put(EweChatTool.MSG_TYPE, Type.TEXT.value);
        Map<String, Object> dataTextMap = new HashMap<>();
        dataTextMap.put(CONTENT, content);

        addSendTo(params, dataTextMap); // 增加发送给用户

        dataMap.put(Type.TEXT.value, dataTextMap);
        return dataMap;
    }

    /**
     * 增加发送给用户
     * 
     * @Title addSendTo
     * @author 吕凯
     * @date 2021年4月16日 上午9:31:03
     * @param noticeIds
     *            id
     * @param noticeMobiles
     *            手机号
     * @param noticeAll
     *            发送给全部
     * @param dataTextMap
     *            数据map
     * @return void
     */
    public static void addSendTo(List<String> noticeIds, List<String> noticeMobiles, boolean noticeAll, Map<String, Object> dataTextMap) {
        if (ListUtils.isNotEmpty(noticeIds)) {
            dataTextMap.put(USERID_LIST, noticeIds);
        }
        if (ListUtils.isNotEmpty(noticeMobiles)) {
            dataTextMap.put(MOBILE_LIST, noticeMobiles);
        }
        if (noticeAll) { // 通知全部
            if (ListUtils.isNotEmpty(noticeIds)) { // id增加@all
                noticeIds.add(EweChatTool.SEND_TO_ALL);
                dataTextMap.put(USERID_LIST, noticeIds);
            }
            if (ListUtils.isNotEmpty(noticeMobiles)) { // 手机号增加@all
                noticeMobiles.add(EweChatTool.SEND_TO_ALL);
                dataTextMap.put(MOBILE_LIST, noticeMobiles);
            }
            if (ListUtils.isEmpty(noticeIds) && ListUtils.isEmpty(noticeMobiles)) { // 都没有时，增加id@all
                noticeIds = new ArrayList<>();
                noticeIds.add(EweChatTool.SEND_TO_ALL);
                dataTextMap.put(USERID_LIST, noticeIds);
            }
        }
    }

    /**
     * 添加发送到用户的信息
     * 
     * @Title addSendTo
     * @author 吕凯
     * @date 2021年4月16日 上午9:43:29
     * @param params
     * @param dataTextMap
     *            void
     */
    public static void addSendTo(EweChatWebHookMsgParams params, Map<String, Object> dataTextMap) {
        String noticeIdsStr = params.getNoticeIds();
        String noticeMobilesStr = params.getNoticeMobiles();
        Boolean noticeAll = params.getNoticeAll(); // 是否发送给全体
        if (noticeAll == null) {
            noticeAll = false;
        }
        List<String> noticeIds = noticeIdsStr == null ? null : Arrays.asList(noticeIdsStr.split(","));
        List<String> noticeMobiles = (noticeMobilesStr == null ? null : Arrays.asList(noticeMobilesStr.split(",")));

        addSendTo(noticeIds, noticeMobiles, noticeAll, dataTextMap);
    }

    /**
     * 发送文本消息
     * 
     * @Title sendText
     * @author 吕凯
     * @date 2021年4月15日 下午5:41:21
     * @param key
     * @param content
     *            内容
     * @param noticeAll
     *            是否通知全体人员
     * @return JSONObject
     */
    public static final Result sendTextMsg(String key, String content, boolean noticeAll) {
        Map<String, Object> dataMap = parse2TextDataMap(content, noticeAll);
        return sendMsg(key, dataMap);
    }

    /**
     * 发送文本消息
     * 
     * @Title sendTextMsg
     * @author 吕凯
     * @date 2021年4月16日 下午12:05:25
     * @param content
     * @param noticeAll
     * @return JSONObject
     */
    public static final Result sendTextMsg(String content, boolean noticeAll) {
        return sendTextMsg(getConfigKey(), content, noticeAll);
    }

    /**
     * 发送消息，默认不@全部
     * 
     * @Title sendTextMsg
     * @author 吕凯
     * @date 2021年4月16日 下午12:37:22
     * @param content
     * @return JSONObject
     */
    public static final Result sendTextMsg(String content) {
        return sendTextMsg(content, false);
    }

    public static final Result sendTextMsg(EweChatWebHookMsgParams params) {
        return sendTextMsg(getConfigKey(), params);
    }

    public static final Result sendTextMsg(String key, EweChatWebHookMsgParams params) {
        Map<String, Object> dataMap = parse2TextDataMap(params);
        return sendMsg(key, dataMap);
    }

    // ============================================= 图文消息 ==============================================
    /**
     * 
     * 转换为图文消息的map
     * 
     * @Title parse2NewsDataMap
     * @author 吕凯
     * @date 2020年7月7日 上午8:21:09
     * @param title
     *            标题，不超过128个字节，超过会自动截断
     * @param description
     *            描述，不超过512个字节，超过会自动截断
     * @param url
     *            点击调整链接
     * @param picurl
     *            图片链接
     * @return Map<String,Object>
     */
    public static final Map<String, Object> parse2NewsDataMap(String title, String description, String url, String picurl) {
        /*{
            MSG_TYPE: "news",
            "news": {
               "articles" : [
                   {
                       "title" : "中秋节礼品领取",
                       "description" : "今年中秋节公司有豪礼相送",
                       "url" : "www.qq.com",
                       "picurl" : "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png"
                   }
                ]
            }
        }*/
        /*msgtype 是   消息类型，此时固定为news
        articles    是   图文消息，一个图文消息支持1到8条图文
        title   是   标题，不超过128个字节，超过会自动截断
        description 否   描述，不超过512个字节，超过会自动截断
        url 是   点击后跳转的链接。
        picurl  否   图文消息的图片链接，支持JPG、PNG格式，较好的效果为大图 1068*455，小图150*150。*/

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put(EweChatTool.MSG_TYPE, Type.NEWS.value);

        Map<String, List<Map<String, String>>> dataNewsMap = new HashMap<>();
        List<Map<String, String>> newsArtiList = new ArrayList<>();
        Map<String, String> dataTextMap = new HashMap<>();
        dataTextMap.put("title", title);
        if (StringUtils.isNotEmpty(description)) {
            dataTextMap.put("description", description);
        }
        dataTextMap.put("url", url); // 必须有
        dataTextMap.put("picurl", picurl);

        newsArtiList.add(dataTextMap);

        dataNewsMap.put("articles", newsArtiList);
        dataMap.put(Type.NEWS.value, dataNewsMap);
        return dataMap;
    }

    public static final Map<String, Object> parse2NewsDataMap(List<NewsMsgModel> newsMsgList) {
        /*{
            MSG_TYPE: "news",
            "news": {
               "articles" : [
                   {
                       "title" : "中秋节礼品领取",
                       "description" : "今年中秋节公司有豪礼相送",
                       "url" : "www.qq.com",
                       "picurl" : "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png"
                   }
                ]
            }
        }*/
        /*msgtype 是   消息类型，此时固定为news
        articles    是   图文消息，一个图文消息支持1到8条图文
        title   是   标题，不超过128个字节，超过会自动截断
        description 否   描述，不超过512个字节，超过会自动截断
        url 是   点击后跳转的链接。
        picurl  否   图文消息的图片链接，支持JPG、PNG格式，较好的效果为大图 1068*455，小图150*150。*/

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put(EweChatTool.MSG_TYPE, Type.NEWS.value);

        Map<String, List<NewsMsgModel>> dataNewsMap = new HashMap<>();

        dataNewsMap.put("articles", newsMsgList);
        dataMap.put(Type.NEWS.value, dataNewsMap);
        return dataMap;
    }

    public static final Result sendNewsMsg(String key, String title, String description, String url, String picurl) {
        Map<String, Object> dataMap = parse2NewsDataMap(title, description, url, picurl);
        return sendMsg(key, dataMap);
    }

    /**
     * 发送图文消息
     * 
     * @Title sendNewsMsg
     * @author 吕凯
     * @date 2021年4月16日 下午12:03:09
     * @param title
     *            标题
     * @param description
     *            描述
     * @param url
     *            点击的连接
     * @param picurl
     *            图片地址
     * @return JSONObject
     */
    public static final Result sendNewsMsg(String title, String description, String url, String picurl) {
        return sendNewsMsg(getConfigKey(), title, description, url, picurl);
    }

    public static final Result sendNewsMsg(String key, List<NewsMsgModel> newsMsgList) {
        Map<String, Object> dataMap = parse2NewsDataMap(newsMsgList);
        return sendMsg(key, dataMap);
    }

    public static final Result sendNewsMsg(List<NewsMsgModel> newsMsgList) {
        return sendNewsMsg(getConfigKey(), newsMsgList);
    }

    // ============================================= Markdown消息 ==============================================
    /**
     * 转换为markdown格式的消息map
     * 
     * @Title parse2MarkdownDataMap
     * @author 吕凯
     * @date 2020年7月6日 上午8:34:38
     * @param content
     *            内容，支持@某个人，最长不超过4096个字节，必须是utf8编码
     * @return Map<String,Object>
     */
    public static final Map<String, Object> parse2MarkdownDataMap(String content) {
        /*{
            MSG_TYPE: "markdown",
            "markdown": {
                CONTENT: "实时新增用户反馈<font color=\"warning\">132例</font>，请相关同事注意。\n
                 >类型:<font color=\"comment\">用户反馈</font>
                 >普通用户反馈:<font color=\"comment\">117例</font>
                 >VIP用户反馈:<font color=\"comment\">15例</font>"
            }
        }*/
        /*参数    是否必填    说明
        msgtype 是   消息类型，此时固定为markdown
        content 是   markdown内容，最长不超过4096个字节，必须是utf8编码*/
        /*标题 （支持1至6级标题，注意#与文字中间要有空格）
                    # 标题一
                    ## 标题二
                    ### 标题三
                    #### 标题四
                    ##### 标题五
                    ###### 标题六
                    加粗
                    **bold**
                    链接
                    [这是一个链接](http://work.weixin.qq.com/api/doc)
                    行内代码段（暂不支持跨行）
                    `code`
                    引用
                    > 引用文字
                    字体颜色(只支持3种内置颜色)
                    <font color="info">绿色</font>
                    <font color="comment">灰色</font>
                    <font color="warning">橙红色</font>*/

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put(EweChatTool.MSG_TYPE, Type.MARKDOWN.value);

        Map<String, String> dataContentMap = new HashMap<>();
        dataContentMap.put(CONTENT, content);
        dataMap.put(Type.MARKDOWN.value, dataContentMap);
        return dataMap;
    }

    /**
     * 
     * 发送markdown格式的消息
     * 
     * @Title sendMarkdownMsg
     * @author 吕凯
     * @date 2021年8月16日 下午4:10:45
     * @param key
     *            发送消息的key
     * @param content
     *            内容，支持@某个人，最长不超过4096个字节，必须是utf8编码
     * @return Result
     */
    public static final Result sendMarkdownMsg(String key, String content) {
        Map<String, Object> dataMap = parse2MarkdownDataMap(content);
        return sendMsg(key, dataMap);
    }

    /**
     * 发送markdown格式的消息
     * 
     * @Title sendMarkdownMsg
     * @author 吕凯
     * @date 2021年4月16日 下午12:03:09
     * @param content
     *            内容，支持@某个人，最长不超过4096个字节，必须是utf8编码
     * @return JSONObject
     */
    public static final Result sendMarkdownMsg(String content) {
        return sendMarkdownMsg(getConfigKey(), content);
    }

    // ============================================= 图片消息 ==============================================
    /**
     * 转换为图片信息map
     * 
     * @Title parse2ImageDataMap
     * @author 吕凯
     * @date 2020年7月6日 上午8:43:24
     * @param base64Data
     *            图片的base64内容
     * @param md5
     *            图片的md5码
     * @return Map<String,Object>
     */
    public static final Map<String, Object> parse2ImageDataMap(String base64Data, String md5) {
        /*{
            MSG_TYPE: "image",
            "image": {
                "base64": "DATA",
                "md5": "MD5"
            }
        }*/
        /*参数    是否必填    说明
        msgtype 是   消息类型，此时固定为image
        base64  是   图片内容的base64编码
        md5 是   图片内容（base64编码前）的md5值*/

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put(EweChatTool.MSG_TYPE, Type.IMAGE.value);

        Map<String, Object> dataContentMap = new HashMap<>();
        dataContentMap.put("base64", base64Data);
        dataContentMap.put("md5", md5);
        dataMap.put(Type.IMAGE.value, dataContentMap);
        return dataMap;
    }

    /**
     * 
     * 转换为图片信息map
     * 
     * @Title parse2ImageDataMap
     * @author 吕凯
     * @date 2020年7月6日 上午10:10:45
     * @param file
     * @return Map<String,Object>
     */
    public static final Map<String, Object> parse2ImageDataMap(File file) {
        String base64Data = FileUtils.convertFile2Base64(file);
        String md5 = FileUtils.getFileMD5Hex(file);
        return parse2ImageDataMap(base64Data, md5);
    }

    /**
     * 转换为图片信息map
     * 
     * @Title parse2ImageDataMap
     * @author 吕凯
     * @date 2020年7月6日 上午10:10:26
     * @param filePath
     *            文件路径
     * @return Map<String,Object>
     */
    public static final Map<String, Object> parse2ImageDataMap(String filePath) {
        String base64Data = FileUtils.convertFile2Base64(filePath);
        String md5 = FileUtils.getFileMD5Hex(filePath);
        return parse2ImageDataMap(base64Data, md5);
    }

    /**
     * 转换为图片信息map
     * 
     * @Title parse2ImageDataMap
     * @author 吕凯
     * @date 2020年7月6日 上午10:10:16
     * @param urlpath
     * @return Map<String,Object>
     */
    public static final Map<String, Object> parse2ImageDataMap(URL urlpath) {
        String base64Data = FileUtils.convertFile2Base64(urlpath);
        String md5 = FileUtils.getFileMD5Hex(urlpath);
        return parse2ImageDataMap(base64Data, md5);
    }

    public static final Result sendImageMsg(String key, File file) {
        Map<String, Object> dataMap = parse2ImageDataMap(file);
        return sendMsg(key, dataMap);
    }

    /**
     * 发送图片消息
     * 
     * @Title sendImageMsg
     * @author 吕凯
     * @date 2021年4月16日 下午12:03:09
     * @param content
     *            内容
     * @return JSONObject
     */
    public static final Result sendImageMsg(File file) {
        return sendImageMsg(getConfigKey(), file);
    }

    public static final Result sendImageMsg(String key, String filePath) {
        Map<String, Object> dataMap = parse2ImageDataMap(filePath);
        return sendMsg(key, dataMap);
    }

    /**
     * 发送图片消息
     * 
     * @Title sendImageMsg
     * @author 吕凯
     * @date 2021年4月16日 下午12:03:09
     * @param content
     *            内容
     * @return JSONObject
     */
    public static final Result sendImageMsg(String filePath) {
        return sendImageMsg(getConfigKey(), filePath);
    }

    public static final Result sendImageMsg(String key, URL urlpath) {
        Map<String, Object> dataMap = parse2ImageDataMap(urlpath);
        return sendMsg(key, dataMap);
    }

    /**
     * 发送图片消息
     * 
     * @Title sendImageMsg
     * @author 吕凯
     * @date 2021年4月16日 下午12:03:09
     * @param content
     *            内容
     * @return JSONObject
     */
    public static final Result sendImageMsg(URL urlpath) {
        return sendImageMsg(getConfigKey(), urlpath);
    }

    // ============================================= 文件上传及文件消息 ==============================================
    /**
     * 转换为文件信息map，一般需要结合sendFileMsg方法
     * 
     * @Title parse2FileDataMap
     * @author 吕凯
     * @date 2021年4月19日 下午2:28:37
     * @param mediaId
     * @return Map<String,Object>
     */
    public static final Map<String, Object> parse2FileDataMap(String mediaId) {
        /*示例{
            "msgtype": "file",
            "file": {
                 "media_id": "3a8asd892asd8asd"
            }
        }*/
        /*参数    是否必填    说明
        msgtype 是   消息类型，此时固定为image
        base64  是   图片内容的base64编码
        md5 是   图片内容（base64编码前）的md5值*/

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put(EweChatTool.MSG_TYPE, Type.FILE.value);

        Map<String, Object> dataContentMap = new HashMap<>();
        dataContentMap.put(UPLOAD_RESULT_FILE_ID, mediaId);
        dataMap.put(Type.FILE.value, dataContentMap);
        return dataMap;
    }

    /**
     * 发送消息
     * 
     * @Title sendMsg
     * @author 吕凯
     * @date 2021年4月15日 下午5:43:02
     * @param key
     * @param dataMap
     * @return JSONObject
     */
    @SuppressWarnings("unchecked")
    public static final Result sendMsg(String key, Map<String, Object> dataMap) {
        Result result = new Result();
        String url = String.format(URL_SEND, key);

        EweChatLogModel.EweChatLogModelBuilder<?, ?> builder = EweChatLogModel.builder();

        String msgType = (String) dataMap.get(EweChatTool.MSG_TYPE);
        Map<String, Object> contentMap = (Map<String, Object>) dataMap.get(msgType);
        String content = null;
        String sendTo = null;
        if (contentMap != null) {
            content = (String) contentMap.get(CONTENT);
            List<String> send2List = new ArrayList<>();

            List<String> userIdList = (List<String>) contentMap.get(USERID_LIST); // 用户id
            if (ListUtils.isNotEmpty(userIdList)) {
                send2List.addAll(userIdList);
            }
            List<String> mobileList = (List<String>) contentMap.get(MOBILE_LIST); // 手机号增加@all
            if (ListUtils.isNotEmpty(mobileList)) {
                send2List.addAll(mobileList);
            }
            /** 英文字母 、数字和下划线 */
            Pattern GENERAL = Pattern.compile("<@(.+?)>");
            List<String> contendUserList = PatternUtil.getAllGroups(GENERAL, content, false);
            if (ListUtils.isNotEmpty(contendUserList)) {
                send2List.addAll(contendUserList);
            }

            if (ListUtils.isNotEmpty(send2List)) {
                sendTo = String.join(",", send2List);
            }
        }

        builder.type(EweChatTool.WEBHOOK).corpsecret(key).msgType(msgType).sendTo(sendTo).content(content).data(dataMap);

        JSONObject jsonResult = JsoupUtil.sendJson(dataMap, url, EweChatTool.USER_AGENT, 3000);
        // {"errcode":0,"errmsg":"ok"}
        if (jsonResult != null) {
            result.setData(jsonResult);
            Integer errcode = jsonResult.getInteger(EweChatTool.ERRCODE);
            String errmsg = jsonResult.getString(EweChatTool.ERRMSG); // 信息
            result.setMessage(errmsg);

            builder.errcode(errcode).errmsg(errmsg);

            if (errcode == 0) { // 0是成功

                result.setSuccess(true);
                builder.success(EweChatTool.SUCCESS);

            } else {

                result.setSuccess(false);
                builder.success(EweChatTool.ERROR);
            }
        }

        EweChatLogFactory.saveLog(builder.build()); // 调用保存日志的方法，没有实现需要具体项目自己实现

        return result;

    }

    /**
     * 发送消息，key来自配置文件，如果key 的名字不在配置文件或者在配置文件中notice.ewechat.client-secret不是，建议使用{@link #sendMsg(String, Map)}
     * 
     * @Title sendMsg
     * @author 吕凯
     * @date 2021年4月16日 上午8:53:48
     * @param dataMap
     * @return JSONObject
     */
    public static final Result sendMsg(Map<String, Object> dataMap) {
        String key = getConfigKey();
        return sendMsg(key, dataMap);
    }

    /**
     * 上传文件
     * 
     * @Title uploadFile
     * @author 吕凯
     * @date 2021年4月16日 下午3:06:19
     * @param key
     *            key
     * @param file
     *            文件对象
     * @return JSONObject
     */
    public static final Result uploadFile(String key, File file) {
        Result result = new Result();
        String url = String.format(URL_UPLOAD, key, Type.FILE.value);
        JSONObject jsonResult = JsoupUtil.uploadFile(url, file, UPLOAD_FILE_NAME);
        /**
         * 返回结果：{"errcode":0,"errmsg":"ok","media_id":"3QtSsDv5R-shKSFuMM0BWFCauDwhbBmWqbz_uqGT9xO0","created_at":"1618812990","type":"file"}
         */
        if (jsonResult != null) {
            result.setData(jsonResult);
            Integer errcode = jsonResult.getInteger(EweChatTool.ERRCODE);
            String errmsg = jsonResult.getString(EweChatTool.ERRMSG); // 信息
            result.setMessage(errmsg);
            if (errcode == 0) { // 0是成功
                result.setSuccess(true);
            } else {
                result.setSuccess(false);
            }
        }
        return result;
    }

    /**
     * 上传文件
     * 
     * @Title uploadFile
     * @author 吕凯
     * @date 2021年4月16日 下午3:06:59
     * @param file
     * @return JSONObject
     */
    public static final Result uploadFile(File file) {
        return uploadFile(getConfigKey(), file);
    }

    /**
     * 发送文件信息，通过文件对象
     * 
     * @Title sendFileMsg
     * @author 吕凯
     * @date 2021年4月19日 下午2:41:01
     * @param key
     *            密钥
     * @param file
     *            文件
     * @return JSONObject
     */
    public static final Result sendFileMsg(String key, File file) {
        Result result = uploadFile(key, file);
        if (result.getSuccess()) {
            JSONObject jsonObject = (JSONObject) result.getData();
            Map<String, Object> dataMap = parse2FileDataMap(jsonObject.getString(UPLOAD_RESULT_FILE_ID)); // map转换
            return sendMsg(key, dataMap);
        }
        return result;
    }

    public static final Result sendFileMsg(File file) {
        return sendFileMsg(getConfigKey(), file);
    }

    /**
     * 上传文件，通过流
     * 
     * @Title uploadFile
     * @author 吕凯
     * @date 2021年4月19日 下午2:40:14
     * @param key
     *            密钥
     * @param inputStream
     * @param fileName
     *            文件名称
     * @return JSONObject
     */
    public static final JSONObject uploadFile(String key, InputStream inputStream, String fileName) {
        String url = String.format(URL_UPLOAD, key, Type.FILE.value);
        return JsoupUtil.upload(url, inputStream, UPLOAD_FILE_NAME, fileName);
    }

    public static final JSONObject uploadFile(InputStream inputStream, String fileName) {
        return uploadFile(getConfigKey(), inputStream, fileName);
    }

    /**
     * 发送文件，通过流
     * 
     * @Title sendFileMsg
     * @author 吕凯
     * @date 2021年4月19日 下午2:39:50
     * @param key
     *            密钥
     * @param inputStream
     * @param fileName
     *            文件名
     * @return JSONObject
     */
    public static final Result sendFileMsg(String key, InputStream inputStream, String fileName) {
        Result resultReturn = new Result();
        JSONObject result = uploadFile(key, inputStream, fileName);
        Integer errCode = result.getInteger(EweChatTool.ERRCODE);
        if (Integer.valueOf(0).equals(errCode)) {
            Map<String, Object> dataMap = parse2FileDataMap(result.getString(UPLOAD_RESULT_FILE_ID)); // map转换
            return sendMsg(key, dataMap);
        }
        return resultReturn;
    }

    public static final Result sendFileMsg(InputStream inputStream, String fileName) {
        return sendFileMsg(getConfigKey(), inputStream, fileName);
    }

    /**
     * 
     * 转换任务卡片格式dataMap<br>
     * <br>
     * 
     * 投票选择型和多项选择型卡片仅企业微信3.1.12及以上版本支持 <br>
     * 文本通知型、图文展示型和按钮交互型三种卡片仅企业微信3.1.6及以上版本支持（但附件下载功能仍需更新至3.1.12）<br>
     * 
     * 特殊说明<br>
     * 
     * 仅有 按钮交互型、投票选择型、多项选择型 三种类型的卡片支持回调更新或通过接口更新卡片。 按钮交互型、投票选择型、多项选择型 三种类型的卡片可支持用户点击触发交互事件，需要开发者设置的回调接口来处理回调事件，回调协议可见文档 模板卡片事件推送，注意
     * 没有配置回调接口的应用不可发送这三种卡片。 开发者的服务收到回调事件后，需要根据协议返回相应的数据以更新卡片，对应的协议见文档 更新模版卡片消息。
     * 此接口发送这三种卡片消息之后，返回的参数里会带上response_code，可使用response_code调用更新模版卡片消息接口，response_code 24小时内有效，且只能调用一次接口。
     * 
     * @Title parse2TemplateCardDataMap
     * @author 吕凯
     * @date 2021年4月20日 上午9:14:45
     * @param agentid
     * @param sendTo
     * @param taskId
     * @param title
     * @param description
     * @param url
     * @param btns
     * @param duplicateCheckInterval
     *            重复信息检查时间间隔，单位秒，不检查设置为-1，默认600秒，最大4小时
     * @return Map<String,Object>
     */

    public static Map<String, Object> parse2TemplateCardDataMap(TemplateCardBasicModel model) {

        Map<String, Object> dataMap = new HashMap<>();

        dataMap.put(EweChatTool.MSG_TYPE, Type.TEMPLATE_CARD.value);

        SerializeConfig sconfig = new SerializeConfig();
        sconfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
        Map<String, Object> modelMap = JSONObject.parseObject(JSONObject.toJSONString(model, sconfig));
        log.warn("{}", modelMap);

        dataMap.put(Type.TEMPLATE_CARD.value, modelMap);
//        System.out.println(JSONObject.toJSONString(dataMap));
        return dataMap;
    }

    @SuppressWarnings("rawtypes")
    public static Result sendTemplateCardMsg(String key, TemplateCardBasicModel model) {
        if (model == null) {
            return ResultUtil.error("TemplateCardModel为空！");
        }
        if (StringUtils.isBlank(model.getCardType())) {
            return ResultUtil.error("cardType不能为空！");
        }

        if (ModelUtil.hasField(model, "cardAction") && ModelUtil.getModelValue(model, "cardAction") == null) {
            return ResultUtil.error("cardAction不能为空！");
        }
        // 含有此属性，并且不为空，并且条目大于3
        if (ModelUtil.hasField(model, "jumpList") && ModelUtil.getModelValue(model, "jumpList") != null
                && ((List) ModelUtil.getModelValue(model, "jumpList")).size() > 3) {
            return ResultUtil.error("jumpList最多3个！");
        }
        // 含有此属性，并且不为空，并且条目大于4
        if (ModelUtil.hasField(model, "verticalContentList") && ModelUtil.getModelValue(model, "verticalContentList") != null
                && ((List) ModelUtil.getModelValue(model, "verticalContentList")).size() > 4) {
            return ResultUtil.error("verticalContentList最多6个！");
        }
        // 含有此属性，并且不为空，并且条目大于6
        if (ModelUtil.hasField(model, "horizontalContentList") && ModelUtil.getModelValue(model, "horizontalContentList") != null
                && ((List) ModelUtil.getModelValue(model, "horizontalContentList")).size() > 6) {
            return ResultUtil.error("horizontalContentList最多6个！");
        }
        // 图文通知的图片
        if (ModelUtil.hasField(model, "cardImage") && ModelUtil.getModelValue(model, "cardImage") == null) {
            return ResultUtil.error("cardImage不能为空！");
        }
        // 按钮交互型的按钮

        if (ModelUtil.hasField(model, "buttonList") && ModelUtil.getModelValue(model, "buttonList") != null
                && ((List) ModelUtil.getModelValue(model, "buttonList")).size() > 6) {
            return ResultUtil.error("buttonList最多6个！");
        }

        return sendMsg(key, parse2TemplateCardDataMap(model));

    }

    /**
     * 发送文本类型的通知
     * 
     * @Title sendTemplateCardTextMsg
     * @author 吕凯
     * @date 2021年9月13日 上午9:48:33
     * @param corpid
     * @param corpsecret
     * @param agentid
     * @param sendTo
     * @param model
     * @param duplicateCheckInterval
     * @return Result
     */
    /*{
    "touser" : "UserID1|UserID2|UserID3",
    "toparty" : "PartyID1 | PartyID2",
    "totag" : "TagID1 | TagID2",
    "msgtype" : "template_card",
    "agentid" : 1,
    "template_card" : {
        "card_type" : "text_notice",
        "source" : {
            "icon_url": "图片的url",
            "desc": "企业微信"
        },
        "main_title" : {
            "title" : "欢迎使用企业微信",
            "desc" : "您的好友正在邀请您加入企业微信"
        },
        "emphasis_content": {
            "title": "100",
            "desc": "核心数据"
        },
        "sub_title_text" : "下载企业微信还能抢红包！",
        "horizontal_content_list" : [
            {
                "keyname": "邀请人",
                "value": "张三"
            },
            {
                "type": 1,
                "keyname": "企业微信官网",
                "value": "点击访问",
                "url": "https://work.weixin.qq.com"
            },
            {
                "type": 2,
                "keyname": "企业微信下载",
                "value": "企业微信.apk",
                "media_id": "文件的media_id"
            }
        ],
        "jump_list" : [
            {
                "type": 1,
                "title": "企业微信官网",
                "url": "https://work.weixin.qq.com"
            },
            {
                "type": 2,
                "title": "跳转小程序",
                "appid": "小程序的appid",
                "pagepath": "/index.html"
            }
        ],
        "card_action": {
            "type": 2,
            "url": "https://work.weixin.qq.com",
            "appid": "小程序的appid",
            "pagepath": "/index.html"
        }
    },
    "enable_id_trans": 0,
    "enable_duplicate_check": 0,
    "duplicate_check_interval": 1800
    }*/
    public static Result sendTemplateCardTextMsg(String key, TemplateCardTextModel model) {
        if (model != null) {
            model.setCardType(EweChatTool.TEMPL_CARD_TYPE_TEXT);
        }
        return sendTemplateCardMsg(key, model);

    }

    public static Result sendTemplateCardTextMsg(TemplateCardTextModel model) {
        // 注意下面参数需要到企业微信中获取
        return sendTemplateCardTextMsg(getConfigKey(), model);

    }

    /**
     * 发送文本类型的通知模板
     * 
     * @Title sendTemplateCardTextMsg
     * @author 吕凯
     * @date 2021年9月13日 上午9:43:56
     * @param corpid
     * @param corpsecret
     * @param agentid
     * @param sendTo
     * @param title
     *            一级标题，建议不超过36个字
     * @param description
     *            二级普通文本，建议不超过160个字
     * @param url，点击后跳转链接
     * @param duplicateCheckInterval
     * @return Result
     */
    public static Result sendTemplateCardTextMsg(String key, String title, String description, String url) {
        TemplateCardTextModel model = TemplateCardTextModel.builder().cardType(EweChatTool.TEMPL_CARD_TYPE_TEXT)
                .mainTitle(TmplCardMainTitleModel.builder().title(title).build()).subTitleText(description)
                .cardAction(TmplCardActionModel.builder().type(1).url(url).build()).build();
        return sendMsg(key, parse2TemplateCardDataMap(model));

    }

    /**
     * 
     * 发送模板任务卡片消息
     * 
     * @Title sendTaskCardMsg
     * @author 吕凯
     * @date 2021年4月20日 下午6:04:09
     * @param sendTo
     * @param taskId
     * @param title
     * @param description
     * @param url
     * @param btns
     * @param duplicateCheckInterval
     *            重复信息检查时间间隔，单位秒，不检查设置为-1，默认600秒，最大4小时
     * @return Result
     */
    public static Result sendTemplateCardTextMsg(String title, String description, String url) {
        // 注意下面参数需要到企业微信中获取
        return sendTemplateCardTextMsg(getConfigKey(), title, description, url);
    }

    // ========================================== 模板卡片：：图文展示型 ====================================
    /**
     * 发送图文展示型的通知
     * 
     * @Title sendTemplateCardNewsMsg
     * @author 吕凯
     * @date 2021年9月13日 上午9:48:33
     * @param corpid
     * @param corpsecret
     * @param agentid
     * @param sendTo
     * @param model
     * @param duplicateCheckInterval
     * @return Result
     */

    public static Result sendTemplateCardNewsMsg(String key, TemplateCardNewsModel model) {
        if (model != null) {
            model.setCardType(EweChatTool.TEMPL_CARD_TYPE_NEWS);
        }
        return sendTemplateCardMsg(key, model);

    }

    public static Result sendTemplateCardNewsMsg(TemplateCardNewsModel model) {
        // 注意下面参数需要到企业微信中获取
        return sendTemplateCardNewsMsg(getConfigKey(), model);

    }

    /**
     * 发送图文展示型通知模板
     * 
     * @Title sendTemplateCardNewsMsg
     * @author 吕凯
     * @date 2021年9月13日 下午3:03:45
     * @param corpid
     * @param corpsecret
     * @param agentid
     * @param sendTo
     * @param title
     * @param imagePath
     * @param url
     * @param duplicateCheckInterval
     * @return Result
     */
    public static Result sendTemplateCardNewsMsg(String key, String title, String imagePath, String url) {
        TemplateCardNewsModel model = TemplateCardNewsModel.builder().cardType(EweChatTool.TEMPL_CARD_TYPE_NEWS)
                .mainTitle(TmplCardMainTitleModel.builder().title(title).build())
                .cardImage(TmplCardImageModel.builder().url(imagePath).build())
                .cardAction(TmplCardActionModel.builder().type(1).url(url).build()).build();
        return sendMsg(getConfigKey(), parse2TemplateCardDataMap(model));

    }

    /**
     * 
     * 发送模板任务卡片消息
     * 
     * @Title sendTemplateCardNewsMsg
     * @author 吕凯
     * @date 2021年4月20日 下午6:04:09
     * @param sendTo
     * @param taskId
     * @param title
     * @param imagePath
     *            图片路径
     * @param url
     * @param btns
     * @param duplicateCheckInterval
     *            重复信息检查时间间隔，单位秒，不检查设置为-1，默认600秒，最大4小时
     * @return Result
     */
    public static Result sendTemplateCardNewsMsg(String title, String imagePath, String url) {
        // 注意下面参数需要到企业微信中获取
        return sendTemplateCardNewsMsg(getConfigKey(), title, imagePath, url);
    }

    /**
     * 获取配置文件中的key
     * 
     * @Title getConfigKey
     * @author 吕凯
     * @date 2021年4月16日 下午2:36:25
     * @return String
     */
    private static String getConfigKey() {
        String key = PropertiesModel.CONFIG.getString(CONFIG_KEY);
        return key;
    }

}
